package com.linln.admin.system.service.impl;

import com.linln.admin.system.domain.Schedule;
import com.linln.admin.system.repository.ScheduleRepository;
import com.linln.admin.system.service.ScheduleService;
import com.linln.common.constant.StatusConst;
import com.linln.common.data.PageSort;
import com.linln.common.enums.StatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;

/**
 * @author taofucheng
 * @date 2022/09/03
 */
@Slf4j
@Service
public class ScheduleServiceImpl implements ScheduleService, InitializingBean {
    private static final String TRIGGER_KEY_PRE = "SYS_SCHD_";
    private static final String SYS_SCHD_GROUP = "SYS_SCHEDULE";
    public static final String JOB_ENTITY_KEY = "schedulEntity";

    @Autowired
    private ScheduleRepository scheduleRepository;
    @Autowired
    private Scheduler scheduler;

    /**
     * 根据ID查询数据
     *
     * @param id 主键ID
     */
    @Override
    public Schedule getById(Long id) {
        return scheduleRepository.findById(id).orElse(null);
    }

    /**
     * 获取分页列表数据
     *
     * @param example 查询实例
     * @return 返回分页数据
     */
    @Override
    public Page<Schedule> getPageList(Example<Schedule> example) {
        // 创建分页对象
        PageRequest page = PageSort.pageRequest();
        return scheduleRepository.findAll(example, page);
    }

    /**
     * 保存数据
     *
     * @param schedule 实体对象
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Schedule save(Schedule schedule) {
        try {
            new CronExpression(schedule.getCron());
        } catch (Exception e) {
            throw new IllegalArgumentException("正则表达式不正确！");
        }
        Schedule ret = scheduleRepository.save(schedule);
        //删除原来的定时计划，增加本次的计划
        removeTrigger(schedule);
        addTrigger(schedule);
        return ret;
    }

    private void addTrigger(Schedule schedule) {
        try {
            JobDetail job = JobBuilder.newJob(ScheduleJobDetail.class).withIdentity(JobKey.jobKey(TRIGGER_KEY_PRE + schedule.getId(), SYS_SCHD_GROUP)).build();
            JobDataMap jobDataMap = new JobDataMap();
            jobDataMap.put("jobDetail", job);
            jobDataMap.put(JOB_ENTITY_KEY, schedule);

            CronTriggerImpl trigger = new CronTriggerImpl();
            trigger.setName(job.getKey().getName());
            trigger.setGroup(job.getKey().getGroup());
            trigger.setJobKey(job.getKey());
            trigger.setJobDataMap(jobDataMap);
            trigger.setStartTime(new Date());
            trigger.setCronExpression(schedule.getCron());
            trigger.setDescription(schedule.getTitle());
            scheduler.scheduleJob(job, trigger);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new IllegalArgumentException("创建定时调度失败！");
        }
    }

    private void removeTrigger(Schedule schedule) {
        try {
            scheduler.deleteJob(JobKey.jobKey(TRIGGER_KEY_PRE + schedule.getId(), SYS_SCHD_GROUP));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new IllegalArgumentException("删除定时调度失败！");
        }
    }

    /**
     * 状态(启用，冻结，删除)/批量状态处理
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateStatus(StatusEnum statusEnum, List<Long> idList) {
        Boolean ret = scheduleRepository.updateStatus(statusEnum.getCode(), idList) > 0;
        if (statusEnum != StatusEnum.OK) {
            //删除原来的定时计划
            for (Long id : idList) {
                removeTrigger(getById(id));
            }
        } else {
            for (Long id : idList) {// 将启用的加入调度
                Schedule schedule = getById(id);
                removeTrigger(schedule);
                addTrigger(schedule);
            }
        }
        return ret;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        List<Schedule> schds = scheduleRepository.findByStatus(StatusConst.OK);
        if (schds == null || schds.isEmpty()) {
            return;
        }
        for (Schedule schd : schds) {
            addTrigger(schd);
        }
    }
}